package com.hj.system.config.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hj.system.config.security.base.MyAuthenticationProvider;
import com.hj.system.config.security.base.MyUserDetailsService;
import com.hj.system.utils.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;

//@Configuration
//@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private PersistentTokenRepository persistentTokenRepository;
    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;

    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Autowired
    private MyAuthenticationFailHander myAuthenticationFailHander;

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
        web.ignoring().antMatchers("/assets/**", "favicon.ico", "/api/**","/vue/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //基础设置
        http.formLogin() //登录表单
                .loginPage("/sys/login")//登录页面url
                .loginProcessingUrl("/sys/login/form")//登录验证url
//                .defaultSuccessUrl("/sys/index")//成功登录跳转
                .successHandler(myAuthenticationSuccessHandler)//成功登录处理器
                .failureHandler(myAuthenticationFailHander)//失败登录处理器
                // .permitAll();
//                .and().authorizeRequests().antMatchers()
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/sys/login").permitAll()
//                //登录成功后有权限访问所有页面
                .and().authorizeRequests().anyRequest().authenticated()  //所有请求都需要认证
                .and()
                .rememberMe()//记住我功能
                .rememberMeParameter("remember-me").rememberMeCookieName("remember-me")
                .userDetailsService(myUserDetailsService)
                .tokenRepository(persistentTokenRepository)//设置持久化token
                .tokenValiditySeconds(24 * 60 * 60); //记住登录1天(24小时 * 60分钟 * 60秒)
        //关闭csrf跨域攻击防御
        http.csrf().disable();
        //跨域
        http.cors();
        http.headers().frameOptions().sameOrigin();
//        http.csrf().disable();

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //这里要设置自定义认证
        auth.authenticationProvider(myAuthenticationProvider);
    }

    /**
     * BCrypt加密
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 记住我功能，持久化的token服务
     *
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(DataSource dataSource) {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        //数据源设置
        tokenRepository.setDataSource(dataSource);

        //启动的时候创建表，这里只执行一次，第二次就注释掉，否则每次启动都重新创建表
        // tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }




    // 处理登录成功的。
    @Component
    public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
        @Autowired
        private ObjectMapper objectMapper;

        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
                throws IOException, ServletException {
            //什么都不做的话，那就直接调用父类的方法
            /*super.onAuthenticationSuccess(request, response, authentication);*/

            //这里可以根据实际情况，来确定是跳转到页面或者json格式。
            //如果是返回json格式，那么我们这么写
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(ResponseUtil.ok("登录成功")));


            //如果是要跳转到某个页面的，比如我们的那个whoim的则
//            new DefaultRedirectStrategy().sendRedirect(request, response, "/sys/index");
            return;
        }
    }

    //登录失败的
    @Component
    public class MyAuthenticationFailHander extends SimpleUrlAuthenticationFailureHandler {
        @Autowired
        private ObjectMapper objectMapper;
        private Logger logger = LoggerFactory.getLogger(MyAuthenticationFailHander.class);

        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                            AuthenticationException exception) throws IOException {
            // TODO Auto-generated method stub
            logger.info("登录失败");
            //以Json格式返回
//            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            String message = exception.getMessage();
            response.getWriter().write(objectMapper.writeValueAsString(ResponseUtil.error(201, "登录失败," + message)));

        }
    }

}
